<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        :root{
            --squraLen: 200px
        }
        #grid{
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            grid-column-gap: 10px;
        }
        .rect{
            width: var(--squraLen);
            height: var(--squraLen);
            background-color: palegreen;
        }
        .move{
            position: relative;
            top: 100px;
        }
        .scale{
            width: 400px;
            height: 400px;
        }
        .trans{
            transition: transform .6s linear;
        }
    </style>
    <div id="grid">
        <div id="test" class="rect"></div>
        <div id="test2" class="rect"></div>
    </div>
    <script>
        // https://css-tricks.com/animating-layouts-with-the-flip-technique/
        window.addEventListener('DOMContentLoaded', () => {
            (function() {
                const div = document.querySelector('#test');

                // first 起始状态
                const rect1 = div.getBoundingClientRect();

                // do something cause layout or repaint
                // .move top样式导致页面重排(layout)
                div.classList.add('move');

                // last 最终状态
                const rect2 = div.getBoundingClientRect();

                // invert： 从“最后”位置出发，将元素反转回“第一个”位置，并使用该转换作为起点
                const invert = rect1.top - rect2.top;

                const ani = div.animate([            
                    { transform: `translateY(${invert}px)`},
                    { transform: `none` }
                ], {
                    duration: 600,
                    easing: 'ease-in-out',
                    fill: 'both'
                });
            })();

            (() => {
                const el = document.querySelector('#test2');

                // first 起始状态
                const rect1 = el.getBoundingClientRect();

                // do something
                // width and height cause page layout（重排）
                el.classList.add('scale');

                // last 最终状态
                const rect2 = el.getBoundingClientRect();

                // invert： 从“最后”位置出发，将元素反转回“第一个”位置，并使用该转换作为起点
                const widthRatio = rect1.width / rect2.width;
                const heightRatio = rect1.height / rect2.height;
                el.style.setProperty('transform-origin', 'top left');

                // 在animate中使用transformOrigin属性
                // 会导致浏览器在每一帧都重新计算元素的位置和大小，这会导致动画卡顿和丢帧
                // 所以，将其在animate之前设置完成，el.style.setProperty('transform-origin', 'top left');
                const ani = el.animate([            
                    { transform: `scale(${widthRatio}, ${heightRatio})` },
                    { transform: 'none' }
                ], {
                    duration: 600,
                    easing: 'linear',
                    fill: 'both'
                });

                // 错误用法
                // const aniError = el.animate([            
                //     { 
                //         transformOrigin: 'top left',
                //         transform: `scale(${widthRatio}, ${heightRatio})`
                //     },
                //     {
                //         transformOrigin: 'top left',
                //         transform: 'none' 
                //     }
                // ], {
                //     duration: 600,
                //     easing: 'linear',
                //     fill: 'both'
                // });
            })();

            // (() => {
            //     const el = document.querySelector('#test2');

            //     // first 起始状态
            //     const rect1 = el.getBoundingClientRect();

            //     // do something
            //     // width and height cause page layout（重排）
            //     el.classList.add('scale');

            //     // last 最终状态
            //     const rect2 = el.getBoundingClientRect();

            //     // invert： 从“最后”位置出发，将元素反转回“第一个”位置，并使用该转换作为起点
            //     const widthRatio = rect1.width / rect2.width;
            //     const heightRatio = rect1.height / rect2.height;
            //     // invert操作
            //     el.style.setProperty('transform-origin', 'top left');
            //     el.style.setProperty('transform', `scale(${widthRatio}, ${heightRatio})`);
            //     // transition动画，需要延长几毫秒才会有效果，所以，一般可以将其放入setTimeout,或者requestAnimationFrame中
            //     requestAnimationFrame(() => {
            //         el.classList.add('trans');
            //         el.style.transform = ''
            //     })
            // })()
        })
    </script>
</body>
</html>